package com.hhkj.admin.course.service.biz.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.hhkj.admin.bank.domain.Bank;
import com.hhkj.admin.bank.domain.BankRule;
import com.hhkj.admin.bank.service.base.BankRuleService;
import com.hhkj.admin.bank.service.base.BankService;
import com.hhkj.admin.course.common.component.checker.CourseChecker;
import com.hhkj.admin.course.common.component.supplier.CourseSupplier;
import com.hhkj.admin.course.common.cont.CourseConst;
import com.hhkj.admin.course.domain.Course;
import com.hhkj.admin.course.model.dopo.CourseDo;
import com.hhkj.admin.course.model.dopo.CoursePo;
import com.hhkj.admin.course.model.req.CourseReq;
import com.hhkj.admin.course.model.req.CourseShowHiddenReq;
import com.hhkj.admin.course.model.res.CourseRes;
import com.hhkj.admin.course.service.base.CourseService;
import com.hhkj.admin.course.service.biz.CourseBizService;
import com.hhkj.admin.course.service.extend.CourseExtendService;
import com.hhkj.admin.student.domain.StudentCourse;
import com.hhkj.admin.student.service.base.StudentCourseService;
import com.ruoyi.common.base.biz.service.biz.BaseBizServiceImpl;
import com.ruoyi.common.cont.SysConst;
import com.ruoyi.common.exception.GlobalException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * CourseBizServiceImpl
 *
 * @author HHKJ-SERVER-ZJ
 * @since 2023/6/8 8:54
 */
@Service
public class CourseBizServiceImpl
        extends BaseBizServiceImpl<Course, CourseReq, CourseRes, CourseDo, CoursePo, CourseService, CourseExtendService>
        implements CourseBizService {
    @Autowired
    private BankService bankService;
    @Autowired
    private BankRuleService bankRuleService;
    @Lazy
    @Autowired
    private StudentCourseService studentCourseService;
    @Autowired
    private CourseSupplier supplier;
    @Autowired
    private CourseChecker checker;

    @Override
    public boolean updateDisable(Course course) {
        Course coursePo = this.getExtendService().getByIdNotNull(course.getId());
        if (coursePo.getDisableFlag().equals(course.getDisableFlag())) {
            if (SysConst.Flag.IS.getFlag().equals(course.getDisableFlag())) {
                throw new GlobalException("已禁用");
            } else {
                throw new GlobalException("已启用");
            }
        }
        LambdaUpdateWrapper<Course> courseUpdate = new LambdaUpdateWrapper<>();
        courseUpdate.eq(Course::getId, course.getId())
                .set(Course::getDisableFlag, course.getDisableFlag());
        return service.update(courseUpdate);
    }

    @Override
    public List<Course> listNodeTreeAll() {
        List<Course> coursePoAllList = service.list(
                new LambdaQueryWrapper<Course>()
                        .orderByAsc(Course::getOrderNum));
        Map<Long, List<Course>> courseListGroupByParentId = coursePoAllList.stream()
                .collect(Collectors.groupingBy(Course::getParentId));
        coursePoAllList.forEach(coursePo ->
                coursePo.setChildren(courseListGroupByParentId.get(coursePo.getId())));
        return coursePoAllList;
    }

    @Override
    public List<Course> allList(List<Long> idList) {
        return service.list(
                new LambdaQueryWrapper<Course>()
                        .eq(Course::getDisableFlag, SysConst.Flag.NO.getFlag())
                        .in(CollectionUtil.isNotEmpty(idList), Course::getId, idList)
                        .orderByAsc(Course::getOrderNum));
    }

    @Override
    public List<Course> listTreeAll(boolean isSupplyBankType, boolean isIncludeDisable) {
        List<Course> coursePoAllList = service.list(
                new LambdaQueryWrapper<Course>()
                        .eq(!isIncludeDisable, Course::getDisableFlag, SysConst.Flag.NO).eq(Course::getNeedHiddenFlag, SysConst.Flag.NO.getFlag())
                        .orderByAsc(Course::getOrderNum));
        if (isSupplyBankType) {
            supplier.supplyBankTypeList(coursePoAllList);
        }
        supplier.supplyCoursePrice(coursePoAllList);
        Map<Long, List<Course>> courseListGroupByParentId = coursePoAllList.stream()
                .collect(Collectors.groupingBy(Course::getParentId));
        coursePoAllList.forEach(coursePo ->
                coursePo.setChildren(courseListGroupByParentId.get(coursePo.getId())));

        return coursePoAllList
                .stream()
                .filter(coursePo ->
                        CourseConst.ROOT_PARENT_ID.equals(coursePo.getParentId()))
                .collect(Collectors.toList());
    }

    @Override
    public boolean hiddenShowCourse(CourseShowHiddenReq req) {
        // 是否存在该课程
        Course existCourse = extendService.getByIdNotDisable(req.getId());
        // 拿到整个课程树,根据id获取到需要操作的课程元素
        List<Course> coursePoAllList = service.list(
                new LambdaQueryWrapper<Course>()
                        .eq(Course::getDisableFlag, SysConst.Flag.NO)
                        .orderByAsc(Course::getOrderNum));

        Map<Long, List<Course>> courseListGroupByParentId = coursePoAllList.stream()
                .collect(Collectors.groupingBy(Course::getParentId));
        coursePoAllList.forEach(coursePo ->
                coursePo.setChildren(courseListGroupByParentId.get(coursePo.getId())));
        Optional<Course> optionalCourse = coursePoAllList.stream().filter(e -> req.getId().equals(e.getId())).findFirst();
        Set<Long> courseIdSet = Sets.newHashSet(req.getId());
        optionalCourse.ifPresent(course -> findChildrenIds(course, courseIdSet));
        // 如果隐藏的是最后一个子元素,连同父级节点都隐藏 递归获取父节点是否隐藏
        Set<Long> needHiddenShowParentIdSet = Sets.newHashSet();
        parentCourseHandler(existCourse, courseListGroupByParentId, req.getNeedHiddenFlag(), needHiddenShowParentIdSet, coursePoAllList);
        courseIdSet.addAll(needHiddenShowParentIdSet);
        LambdaUpdateWrapper<Course> courseUpdate = new LambdaUpdateWrapper<>();
        courseUpdate.set(Course::getNeedHiddenFlag, req.getNeedHiddenFlag()).in(Course::getId, courseIdSet);
        return this.getService().update(courseUpdate);
    }


    @Override
    protected void beforeSave(CourseReq req) {
        // 1、检测上级课程是否存在
        if (ObjectUtil.isNotNull(req.getParentId())
                && !CourseConst.ROOT_PARENT_ID.equals(req.getParentId())) {
            Course parentCoursePo = service.getById(req.getParentId());
            if (ObjectUtil.isNull(parentCoursePo)) {
                throw new GlobalException("上级课程信息不存在");
            }
            // 3、配置levelNum
            req.setLevelNum((parentCoursePo.getLevelNum() + 1));
        }
        //中文括号统一转为英文
        req.setName(req.getName().replace("（", "(").replace("）", ")"));
        checker.checkCourseNameRepeat(req);
        // 20240816去除排序默认值 改为自定义设置排序
        // 2、获取当前级别下最大orderNum课程信息，设置课程orderNum
        /*LambdaQueryWrapper<Course> courseQuery = new LambdaQueryWrapper<>();
        courseQuery.eq(Course::getParentId, req.getParentId())
                .orderByDesc(Course::getOrderNum);
        Course maxOrderCourse = this.getService().getOne(courseQuery, false);
        req.setOrderNum(ObjectUtil.isNull(maxOrderCourse) ? 1 : (maxOrderCourse.getOrderNum() + 1));*/
    }

    @Override
    protected void beforeUpdate(CourseReq req) {
        this.getExtendService().getByIdNotNull(req.getId());
        checker.checkCourseNameRepeat(req);
    }

    @Override
    protected void beforeRemove(List<Long> idList) {
        // 查看是否有下一级
        LambdaQueryWrapper<Course> courseQuery = new LambdaQueryWrapper<>();
        courseQuery.in(Course::getParentId, idList);
        long childrenAmount = this.getService().count(courseQuery);
        if (childrenAmount > 0L) {
            throw new GlobalException("所选课程有下一级课程，无法删除");
        }
        // 查看是否有题库
        long bankAmount = bankService.count(new LambdaQueryWrapper<Bank>()
                .in(Bank::getCourseId, idList));
        if (bankAmount > 0L) {
            throw new GlobalException("课程存在题库信息，无法删除");
        }
        // 查看是否有选题配置信息
        long bankRuleAmount = bankRuleService.count(new LambdaQueryWrapper<BankRule>()
                .and(eq -> eq.in(BankRule::getCourseId, idList)
                        .or()
                        .in(BankRule::getCourseParentId, idList)));
        if (bankRuleAmount > 0L) {
            throw new GlobalException("课程存在选题规则配置信息，无法删除");
        }
        // 查看是都有对应学员
        long studentAmount = studentCourseService.count(new LambdaQueryWrapper<StudentCourse>()
                .in(StudentCourse::getCourseId, idList));
        if (studentAmount > 0L) {
            throw new GlobalException("课程存在学员信息，无法删除");
        }
    }

    /**
     * 递归查找所有子元素的 id
     *
     * @param courseTree 课程树的根节点
     * @param ids        所有子元素的 id 列表
     */
    private void findChildrenIds(Course courseTree, Set<Long> ids) {
        if (courseTree == null || ids == null) {
            return;
        }
        if (CollectionUtil.isEmpty(courseTree.getChildren())) {
            return;
        }
        // 遍历每个节点
        for (Course course : courseTree.getChildren()) {
            // 如果找到匹配的父 id，添加当前节点的 id 并继续递归查找子节点
            ids.add(course.getId());
            findChildrenIds(course, ids);
        }
    }

    /**
     * 处理父课程的显示逻辑
     * 此方法的目的是根据课程是否需要隐藏的标志，来决定其父课程是否应该显示
     * 它通过递归地检查每个课程的兄弟课程（同一父课程下的其他课程）来确定是否需要显示父课程
     *
     * @param existCourse 当前存在的课程，用于检查其父课程是否应该显示
     * @param courseListGroupByParentId 按父课程ID分组的课程列表，用于查找特定父课程下的所有课程
     * @param needHiddenFlag 需要隐藏的标志，用于判断课程是否需要被隐藏
     * @param needHiddenShowParentIdSet 需要显示的父课程ID集合，如果一个父课程下所有子课程都需要隐藏，则该父课程ID将被添加到此集合
     * @param coursePoAllList 所有课程列表，用于查找特定课程的信息
     */
    private void parentCourseHandler(Course existCourse, Map<Long, List<Course>> courseListGroupByParentId, Integer needHiddenFlag, Set<Long> needHiddenShowParentIdSet, List<Course> coursePoAllList) {
        Integer invertHiddenFlag = SysConst.Flag.IS.getFlag().equals(needHiddenFlag) ? SysConst.Flag.NO.getFlag() : SysConst.Flag.IS.getFlag();
        Optional<Course> optionalCourse = coursePoAllList.stream().filter(e -> existCourse.getParentId().equals(e.getId())).findFirst();
        if (!optionalCourse.isPresent() || CourseConst.ROOT_PARENT_ID.equals(existCourse.getParentId())) {
            return;
        }
        List<Course> courseList = courseListGroupByParentId.get(existCourse.getParentId());
        Set<Long> childrenIdSet = courseList.stream().filter(e -> !e.getId().equals(existCourse.getId()) && invertHiddenFlag.equals(e.getNeedHiddenFlag())).map(Course::getId).collect(Collectors.toSet());
        if (CollectionUtil.isNotEmpty(childrenIdSet)) {
           return;
        }
        needHiddenShowParentIdSet.add(existCourse.getParentId());
        parentCourseHandler(optionalCourse.get(), courseListGroupByParentId, needHiddenFlag, needHiddenShowParentIdSet, coursePoAllList);
    }
}
